package com.zoe.onelink.dubbo.filter;

import com.zoe.onelink.common.enumeration.ExceptionCode;
import com.zoe.onelink.common.enumeration.ExceptionCodeEnum;
import com.zoe.onelink.common.enumeration.bean.ExceptionCodeBean;
import com.zoe.onelink.common.exception.ApiException;
import com.zoe.onelink.dubbo.provider.DubboI18nMessageProvider;
import com.zoe.onelink.dubbo.util.ArrayUtil;
import com.zoe.onelink.util.StackTraceUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.service.GenericService;

import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2021</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2021-08-04
 */
@Activate(group = CommonConstants.PROVIDER, order = 100)
@Slf4j
public class ProviderExceptionFilter implements Filter, Filter.Listener {

    public static final Set<String> IGNORED_STACK_TRACE_CLASS_NAME_PREFIX_SET;
    public static final int STACK_TRACE_THRESHOLD = 30;

    private DubboI18nMessageProvider dubboI18nMessageProvider;

    public void setDubboI18nMessageProvider(DubboI18nMessageProvider dubboI18nMessageProvider) {
        this.dubboI18nMessageProvider = dubboI18nMessageProvider;
    }

    static {
        IGNORED_STACK_TRACE_CLASS_NAME_PREFIX_SET = Stream.of(
                "org.apache.catalina",
                "org.apache.dubbo.rpc",
                "org.springframework"
        ).collect(Collectors.toSet());
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        return invoker.invoke(invocation);
    }

    @Override
    public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {

        // 如果返回结果不包含异常或者是通用service,直接返回
        if (!appResponse.hasException() || GenericService.class == invoker.getInterface()) {
            return;
        }

        try {

            // 获取当前返回结果异常
            Throwable exception = appResponse.getException();

            // 既不是运行时异常,也不是检查异常,直接返回
            if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
                return;
            }
            ExceptionCode apiExceptionCode;
            String exMessage;
            // 业务异常国际化
            if (exception instanceof ApiException && this.dubboI18nMessageProvider != null) {
                ApiException apiException = (ApiException) exception;
                apiExceptionCode = new ExceptionCodeBean(apiException.getExceptionCode());
                exMessage = this.dubboI18nMessageProvider.getMessage(apiExceptionCode, apiException.getParams());
            } else {
                exMessage = exception.getMessage();
                apiExceptionCode = new ExceptionCodeBean(ExceptionCodeEnum.RPC_ERROR);
            }

            // 当前堆栈
            StackTraceElement[] currentStackTrace = Thread.currentThread().getStackTrace();
            // 服务提供者堆栈
            StackTraceElement[] providerStackTrace = exception.getStackTrace();
            // 合并后的堆栈
            StackTraceElement[] mergedStackTrace = ArrayUtil.addAll(currentStackTrace, providerStackTrace);
            // 如果超过堆栈数量阈值,进行裁剪
            if (mergedStackTrace.length > STACK_TRACE_THRESHOLD) {
                StackTraceElement[] filteredStackTrace = StackTraceUtil.cutStackTrace(mergedStackTrace, false);
                exception.setStackTrace(filteredStackTrace);
                if (log.isDebugEnabled()) {
                    log.debug("已自动裁剪掉{}个堆栈信息,原堆栈数量:{},当前堆栈数量:{}", mergedStackTrace.length - filteredStackTrace.length, mergedStackTrace.length, filteredStackTrace.length);
                }
            } else {
                exception.setStackTrace(mergedStackTrace);
            }
            ApiException apiException = ApiException.createEx(exMessage)
                    .setFormattedMessage(exMessage)
                    .setExceptionCode(apiExceptionCode);
            appResponse.setException(apiException);

        } catch (Throwable e) {
            log.warn("调用异常过滤器失败. 主机地址:[{}] , 接口:[{}.{}] , 异常信息:{}:{}", RpcContext.getContext().getRemoteHost(), invoker.getInterface().getName(), invocation.getMethodName(), e.getClass().getName(), e.getMessage(), e);
        }
    }

    @Override
    public void onError(Throwable e, Invoker<?> invoker, Invocation invocation) {
        log.warn("未检查和未声明的异常. 主机地址:[{}] , 接口:[{}.{}] , 异常信息:{}:{}", RpcContext.getContext().getRemoteHost(), invoker.getInterface().getName(), invocation.getMethodName(), e.getClass().getName(), e.getMessage(), e);
    }
}
